module sw.parser;
import std.string, std.stdio;
/**
 * ちゃんとしたパーサ
 */

///コマンド。textかcomplexかはparentで判断するべき。
struct Cmd{
  union{
    string name;//for complex
    string text;//for text
  }
  Cmd*[][] args;//for complex
  Cmd* parent;//for complex
}

private Cmd[] pool;
private uint i=20;
private Cmd* newCmd(){
  if(i==20){
    pool=new Cmd[20];
    i=0;
  }
  return &pool[i++];
}

private Cmd* newTextCmd(string text){
  Cmd* cmd=newCmd;
  cmd.text=text;
  return cmd;
}


///パーサ
static struct Parser{
  static Cmd*[] parse(string source){
    Cmd* re=newCmd;
    re.args.length=1;
    string text;
    Cmd* base=re;
    uint ib,i;
    startloop:for(;i<source.length;i++){
      switch(source[i]){
        case '[':
          if(text.length){
            if(ib!=i)text~=source[ib..i];
            base.args[$-1]~=newTextCmd(text);
            text="";
          }else if(ib!=i)
            base.args[$-1]~=newTextCmd(source[ib..i]);
          ib=i+1;
          Cmd* cmd=newCmd;
          cmd.parent=base;
          base.args[$-1]~=cmd;
          base=cmd;
          i++;
          goto titleloop;
        case '\\':
          if(!text.length)text=source[ib..i];
          else text~=source[ib..i];
          ib=i+1;
          i++;
          break;
        default:
      }
    }
    mainloop:for(;i<source.length;i++){
      switch(source[i]){
        case ',':
          Cmd* cmd;
          if(text.length){
            if(ib!=i)text~=source[ib..i];
            base.args[$-1]~=newTextCmd(text);
            text="";
          }else if(ib!=i)
            base.args[$-1]~=newTextCmd(source[ib..i]);
          ib=i+1;
          base.args.length=base.args.length+1;
          break;
        case '[':
          if(text.length){
            if(ib!=i)text~=source[ib..i];
            base.args[$-1]~=newTextCmd(text);
            text="";
          }else if(ib!=i)
            base.args[$-1]~=newTextCmd(source[ib..i]);
          ib=i+1;
          Cmd* cmd=newCmd;
          cmd.parent=base;
          base.args[$-1]~=cmd;
          base=cmd;
          i++;
          goto titleloop;
        case ']':
          if(text.length){
            text~=source[ib..i];
            base.args[$-1]~=newTextCmd(text);
            text="";
          }else
            if(/*source[ib..i].length!=0*/ib!=i)
              base.args[$-1]~=newTextCmd(source[ib..i]);
          ib=i+1;
          base=base.parent;
          break;
        case '\\':
          if(!text.length)text=source[ib..i];
          else text~=source[ib..i];
          ib=i+1;
          i++;
          break;
        default:
      }
    }
    titleloop:for(;i<source.length;i++){
      switch(source[i]){
        case '\r':
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          break;
        case ' ':
        case '\n':
          base.args.length=1;
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          i++;
          goto mainloop;
        case ']':
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          base=base.parent;
          i++;
          goto mainloop;
        case '\\':
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          i++;
          break;
        default:
      }
    }
    if(text.length){
      text~=source[ib..$];
      base.args[$-1]~=newTextCmd(text);
    }else if(ib!=source.length)
      base.args[$-1]~=newTextCmd(source[ib..$]);
    return re.args[0];
  }
}


///問題無し@20060304
///FIXME: unittestが適当
/+unittest{
  writef("\n\n");
  Cmd[][] test;
  test~=Parser.parse("aa[bb [cc][dd],[ee]]ff\\[test [test\\]\\,test]gg");
//[]なパターン
//[[test] [test]]なパターン([test]という名前のができないといけない)
//\を使ったパターン
//throw new Exception("中断");
  void commandWrite(Cmd[][] cmds){
    foreach(Cmd[] cmd;cmds){
      foreach(Cmd c;cmd){
        if(cast(TextCmd)c is null){
          writef("["~(cast(ComplexCmd)c).name);
          commandWrite((cast(ComplexCmd)c).args);
          writef("]");
          continue;
        }
        writef((cast(TextCmd)c).text);
      }
      writef(",");
    }
  }
  commandWrite(test);
throw new Exception("中断");
}
+/
